from apps.utils.format_json_response import FormatJSONResponse
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.openapi.docs import (
    get_redoc_html,
    get_swagger_ui_html,
    get_swagger_ui_oauth2_redirect_html,
)
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from apps.model import models
from apps.database import engine
from apps.routers import (
    view_new_user, view_books, view_user, view_project, view_task
)
from pydantic import BaseModel
from fastapi_jwt_auth import AuthJWT
from fastapi_jwt_auth.exceptions import AuthJWTException

# 自动创建继承了DbModel的表
models.DbModel.metadata.create_all(bind=engine)


def create_app():
    # 实例化
    app = FastAPI(
        default_response_class=FormatJSONResponse,
        docs_url=None,
        redoc_url=None
    )

    # jwt -- token
    class Settings(BaseModel):
        authjwt_secret_key: str = "secret"    # 生成token的密钥

        # callback to get your configuration

    @AuthJWT.load_config
    def get_config():
        return Settings()

    @app.exception_handler(AuthJWTException)
    def authjwt_exception_handler(request: Request, exc: AuthJWTException):
        return JSONResponse(
            status_code=exc.status_code,
            content={"detail": exc.message}
        )


    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(request: Request, exc: RequestValidationError):
        """
        捕获 请求参数校验失败异常 422
        :param request:
        :param exc:
        :return:
        """
        return JSONResponse(
            status_code=422,
            content=jsonable_encoder({
                "code": 1,
                "msg": "请求参数不合法",
                "data": exc.errors()
            })
        )

    # # 捕获 HTTPException 异常
    # @app.exception_handler(StarletteHTTPException)
    # def http_error(request, exc):
    #     print("捕获到的异常")
    #     print(exc.status_code)
    #     print(exc.detail)
    #     # return JSONResponse({'error_msg': exc.detail}, status_code=exc.status_code)
    #     return PlainTextResponse(content=exc.detail, status_code=exc.status_code)

    @app.exception_handler(StarletteHTTPException)
    async def http_error(request: Request, exc: StarletteHTTPException):
        """
        捕获 HTTPException 异常, 如 404， 405 等
        :param request:
        :param exc:
        :return:
        """
        return JSONResponse(content=jsonable_encoder({
            "code": 2,
            "msg": exc.detail,
            "data": {}
        }), status_code=exc.status_code)


    @app.get("/docs", include_in_schema=False)
    async def custom_swagger_ui_html():
        """解决国内打不开swagger文档问题，引用本地的静态资源"""
        return get_swagger_ui_html(
            openapi_url=app.openapi_url,
            title=app.title + " - Swagger UI",
            oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
            swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js",
            swagger_css_url="/static/swagger-ui/swagger-ui.css",
        )

    @app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
    async def swagger_ui_redirect():
        return get_swagger_ui_oauth2_redirect_html()

    @app.get("/redoc", include_in_schema=False)
    async def redoc_html():
        return get_redoc_html(
            openapi_url=app.openapi_url,
            title=app.title + " - ReDoc",
            redoc_js_url="/static/redoc/bundles/redoc.standalone.js",
        )

    # 挂载静态资源
    app.mount("/static", StaticFiles(directory="static"), name="static")
    app.mount("/web", StaticFiles(directory="web"), name="web")

    # 注册路由
    app.include_router(view_user.router, prefix='/api', tags=['用户'])
    app.include_router(view_new_user.router, prefix='/new', tags=['注册New'])
    app.include_router(view_books.router, prefix='/new', tags=['books'])
    app.include_router(view_project.router, prefix='/api', tags=['项目'])
    app.include_router(view_task.router, prefix='/api', tags=['任务'])

    # 初始化
    @app.on_event('startup')
    def init_scheduler():
        """初始化"""
        from apps.aps import scheduler
        scheduler.start()
        print(f"初始化schedule-------------")
    return app

